using PaintIsland;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    private readonly Vector3 CUBE_OFFSET = new Vector3(0.5f, 0.5f, 0.5f);
    private const float TRIGGER_VIEW_SCALE = 50;

    public static bool UI_SHOWING = false;

    [Header("Internal Links")]
    public Transform headTransform;
    public Transform bodyTransform;
    public Rigidbody bodyRigidbody;
    public Camera playerCamera;

    [Header("Scene Links")]
    public World world;

    [Header("Prefabs")]
    public GameObject locatorPrefab;

    [Header("GUI Links")]
    public Text logText;
    public GameObject pauseUI;

    [Header("Attributes")]
    public float cameraSensitivity;
    public float moveSpeed;
    public float jumpForce;
    public float rotateBodyAngle;

    private GameObject locator;

    private Vector2 viewDelta;
    private Vector3 headEuler;
    public bool isViewModePC;
    private Vector3 bodyEuler;
    private Vector2 move;

    private Vector3Int? targetingBlockPos;
    private Vector3Int? setBlockPos;

    // Start is called before the first frame update
    void Start()
    {
        UI_SHOWING = false;
        headEuler = Vector3.zero;
        bodyEuler = bodyTransform.localEulerAngles;
        locator = Instantiate(locatorPrefab, Vector3.zero, Quaternion.identity);
        locator.SetActive(false);

        TryHideCursor();
    }

    // Update is called once per frame
    void Update()
    {
        TryHideCursor();

        if (!isViewModePC && !UI_SHOWING)
        {
            headEuler += TRIGGER_VIEW_SCALE * cameraSensitivity * Time.deltaTime * new Vector3(-viewDelta.y, viewDelta.x, 0);
            headEuler.x = Mathf.Clamp(headEuler.x, -89, 89);
        }

        Ray ray = playerCamera.ScreenPointToRay(new Vector3(Screen.width / 2.0f, Screen.height / 2.0f));

        if (Physics.Raycast(ray, out RaycastHit hit, 16, LayerMask.GetMask("Chunk")))
        {
            locator.SetActive(true);
            var p = hit.point - 0.2f * hit.normal;
            targetingBlockPos = new Vector3Int(Mathf.FloorToInt(p.x), Mathf.FloorToInt(p.y), Mathf.FloorToInt(p.z));
            locator.transform.position = targetingBlockPos.Value + CUBE_OFFSET;
            var temp = targetingBlockPos.Value + hit.normal;
            setBlockPos = new Vector3Int((int)temp.x, (int)temp.y, (int)temp.z);
        }
        else
        {
            targetingBlockPos = null;
            setBlockPos = null;
            locator.SetActive(false);
        }

        headTransform.localRotation = Quaternion.Euler(headEuler);
        UpdateBodyRotation();

        if (logText != null) logText.text = $"{bodyRigidbody.velocity}";

        bodyTransform.localRotation = Quaternion.Euler(bodyEuler);
    }

    private void UpdateBodyRotation()
    {
        if (headEuler.y - bodyEuler.y > rotateBodyAngle)
        {
            bodyEuler.y = headEuler.y - rotateBodyAngle;
        }
        else if (bodyEuler.y - headEuler.y > rotateBodyAngle)
        {
            bodyEuler.y = headEuler.y + rotateBodyAngle;
        }
    }

    private static void TryHideCursor()
    {
        if (!UI_SHOWING && Application.isFocused)
        {
            Cursor.visible = false;
            Cursor.lockState = CursorLockMode.Locked;
        }
        else
        {
            Cursor.visible = true;
            Cursor.lockState = CursorLockMode.None;
        }
    }

    private void FixedUpdate()
    {
        var forward = new Vector3(headTransform.forward.x, 0, headTransform.forward.z).normalized;
        var velocity = headTransform.right * move.x + forward * move.y;
        velocity.y = Mathf.Clamp(bodyRigidbody.velocity.y, -45, 45);
        bodyRigidbody.velocity = velocity;
    }

    #region Input Callbacks
    public void PlayerView(InputAction.CallbackContext ctx)
    {
        viewDelta = ctx.ReadValue<Vector2>();
        if (ctx.control.device is Mouse)
        {
            headEuler += cameraSensitivity * new Vector3(-viewDelta.y, viewDelta.x, 0) * Time.deltaTime;
            headEuler.x = Mathf.Clamp(headEuler.x, -89, 89);
            isViewModePC = true;
        }
        else
        {
            isViewModePC = false;
        }
    }

    public void PlayerMove(InputAction.CallbackContext ctx)
    {
        move = moveSpeed * ctx.ReadValue<Vector2>().normalized;
    }

    public void PlayerJump(InputAction.CallbackContext ctx)
    {
        if (ctx.performed)
        {
            bodyRigidbody.velocity += Vector3.up * 5;
        }
    }

    public void PlayerPlaceBlock(InputAction.CallbackContext ctx)
    {
        if (UI_SHOWING) return;
        if (setBlockPos is Vector3Int pos && ctx.performed)
        {
            world.PlaceBlock(BlockID.Stone, pos);
        }
    }

    public void PlayerDestroyBlock(InputAction.CallbackContext ctx)
    {
        if (UI_SHOWING) return;
        if (targetingBlockPos is Vector3Int pos && ctx.performed)
        {
            world.PlaceBlock(BlockID.Air, pos);
        }
    }

    public void PlayerPause(InputAction.CallbackContext ctx)
    {
        if (ctx.performed)
        {
            pauseUI.SetActive(true);
            UI_SHOWING = true;
            Time.timeScale = 0;
        }
    }

    public void PlayerResume(InputAction.CallbackContext ctx)
    {
        if (ctx.performed)
        {
            pauseUI.SetActive(false);
            UI_SHOWING = false;
            Time.timeScale = 1;
        }
    }
    #endregion
}
